﻿using Basic.FormExtendService;
using Basic.FormRemoteModel;
using Basic.FormRemoteModel.UserForm.Model;
using Basic.HrRemoteModel.DicItem.Model;
using Basic.UserFormService.Calculate.Interface;
using Basic.UserFormService.Calculate.Model;
using Basic.UserFormService.Model;
using Flee.PublicTypes;
using WeDonekRpc.Client.Attr;
using WeDonekRpc.Helper;

namespace Basic.UserFormService.Calculate
{
    [ClassLifetimeAttr(ClassLifetimeType.SingleInstance)]
    internal class FormDefValueCalculate : IFormDefValueCalculate
    {
        private static readonly Dictionary<FormRecordDataType, Type> _ConvType = new Dictionary<FormRecordDataType, Type>
        {
            {  FormRecordDataType.Time,PublicDataDic.StrType  },
            {  FormRecordDataType.String,PublicDataDic.StrType  },
            { FormRecordDataType.Long,PublicDataDic.LongType },
            { FormRecordDataType.Number, PublicDataDic.DecimalType },
            { FormRecordDataType.DateTime, PublicDataDic.DateTimeType },
            { FormRecordDataType.Date, PublicDataDic.DateTimeType },
            { FormRecordDataType.Bool, PublicDataDic.BoolType },
        };
        private readonly IDictService _Dict;
        public FormDefValueCalculate ( IDictService dict )
        {
            this._Dict = dict;
        }
        public void CalculateForm ( FormBodyDto body, UserFormBody form )
        {
            if ( body.Formula.Length > 0 )
            {
                Dictionary<long, DicItem[]> dictItem = this._Dict.GetItemNames(body.DicId, body.TreeDicId);
                Dictionary<long, UserFormTable> formTable = form.Table.ToDictionary(a => a.Id, a => a);
                this._CalculateForm(body, formTable, dictItem);
            }
        }

        private static bool _AddOrSet ( FillRecordRow row, ColFormula a, object res )
        {
            if ( a.DataType != FormRecordDataType.Array )
            {
                res = Convert.ChangeType(res, _ConvType[a.DataType]);
            }
            if ( !row.Values.TryGetValue(a.Name, out object val) )
            {
                row.Values.Add(a.Name, res);
                return true;
            }
            else if ( _IsEquals(a.DataType, val, res) )
            {
                return false;
            }
            row.Values[a.Name] = res;
            return true;
        }
        private static bool _IsEquals ( FormRecordDataType dataType, object val, object res )
        {
            if ( ( val == null && res != null ) || ( val != null && res == null ) )
            {
                return false;
            }
            if ( dataType == FormRecordDataType.Bool )
            {
                return (bool)val == (bool)res;
            }
            else if ( dataType == FormRecordDataType.String || dataType == FormRecordDataType.Time )
            {
                return (string)val == (string)res;
            }
            else if ( dataType == FormRecordDataType.Long )
            {
                return (long)val == (long)res;
            }
            else if ( dataType == FormRecordDataType.Date || dataType == FormRecordDataType.DateTime )
            {
                return (DateTime)val == (DateTime)res;
            }
            else if ( dataType == FormRecordDataType.Number )
            {
                return (decimal)val == (decimal)res;
            }
            else if ( dataType == FormRecordDataType.Array )
            {
                return ( (string[])val ).IsEqual<string>((string[])res);
            }
            return false;
        }
        private void _CalculateForm ( FormBodyDto body, Dictionary<long, UserFormTable> formTable, Dictionary<long, DicItem[]> dictItem )
        {
            bool isRefr = false;
            body.Formula.ForEach(a =>
            {
                UserFormTable table = formTable[a.TableId];
                if ( table.TableType == FormTableType.单一表单 && table.Row != null )
                {
                    if ( table.Row.Values.TryGetValue(a.Name, out object val) == false || val == null )
                    {
                        object res = this._Calculate(a, formTable, dictItem, body);
                        if ( res != null && _AddOrSet(table.Row, a, res) )
                        {
                            isRefr = true;
                        }
                    }
                }
                else if ( table.Rows.IsNull() == false )
                {
                    ExpressionContext exp = CalculateLinq.Clone();
                    if ( !a.VarCol.IsNull() )
                    {
                        if ( !this._InitVal(a.VarCol, formTable, dictItem, body, exp) )
                        {
                            return;
                        }
                    }
                    table.Rows.ForEach(( c, i ) =>
                    {
                        if ( !c.Values.ContainsKey(a.Name) )
                        {
                            object res = this._Calculate(a, i, exp);
                            if ( res != null && _AddOrSet(c, a, res) )
                            {
                                isRefr = true;
                            }
                        }
                    });
                }
            });
            if ( isRefr )
            {
                this._CalculateForm(body, formTable, dictItem);
            }
        }
        private object _Calculate ( ColFormula formula, int rowIndex, ExpressionContext exp )
        {
            using ( CurState state = CalculateLinq.SetCurrentState(rowIndex) )
            {
                IDynamicExpression expre = exp.CompileDynamic(formula.Formula);
                return expre.Evaluate();
            }
        }

        private object _Calculate ( ColFormula formula,
            Dictionary<long, UserFormTable> table,
            Dictionary<long, DicItem[]> dictItem,
            FormBodyDto body )
        {
            ExpressionContext exp = CalculateLinq.Clone();
            if ( !formula.VarCol.IsNull() )
            {
                if ( !this._InitVal(formula.VarCol, table, dictItem, body, exp) )
                {
                    return null;
                }
            }
            using ( CurState state = CalculateLinq.SetCurrentState(null) )
            {
                IDynamicExpression expre = exp.CompileDynamic(formula.Formula);
                return expre.Evaluate();
            }
        }
        private bool _InitVal ( ColFormulaVar[] vars, Dictionary<long, UserFormTable> formTable, Dictionary<long, DicItem[]> dictItem, FormBodyDto body, ExpressionContext exp )
        {
            return vars.TrueForAll(a =>
            {
                UserFormTable table = formTable[a.TableId];
                if ( table.TableType == FormTableType.单一表单 )
                {
                    if ( table.Row.Values.TryGetValue(a.Name, out object val) && val != null )
                    {
                        exp.Variables[a.Key] = this._FormatValue(val, a, dictItem, body);
                        return true;
                    }
                    return false;
                }
                else if ( table.Rows.IsNull() )
                {
                    return false;
                }
                else
                {
                    object[] datas = table.Rows.ConvertAll(c => c.Values.GetValueOrDefault(a.Name));
                    if ( datas.IsNull() )
                    {
                        return false;
                    }
                    exp.Variables[a.Key] = this._FormatValue(datas, a, dictItem, body);
                    return true;
                }
            });
        }

        private object[] _FormatValue ( object[] value, ColFormulaVar col, Dictionary<long, DicItem[]> dictItem, FormBodyDto body )
        {
            if ( col.FuncType == DataFuncType.树形字典 || col.FuncType == DataFuncType.字典项 )
            {
                return value.ConvertAll<object, object>(a => new RecordVal
                {
                    value = a,
                    dataType = col.DataType,
                    funcType = col.FuncType,
                    dicItem = dictItem[col.DicId.Value]
                });
            }
            else if ( col.FuncType == DataFuncType.选项值 )
            {
                return value.ConvertAll<object, object>(a => new RecordVal
                {
                    value = a,
                    dataType = col.DataType,
                    funcType = col.FuncType,
                    items = body.Items[col.ColId]
                });
            }
            return value;
        }
        private object _FormatValue ( object value, ColFormulaVar col, Dictionary<long, DicItem[]> dictItem, FormBodyDto body )
        {
            if ( col.FuncType == DataFuncType.树形字典 || col.FuncType == DataFuncType.字典项 )
            {
                return new RecordVal
                {
                    value = value,
                    dataType = col.DataType,
                    funcType = col.FuncType,
                    dicItem = dictItem[col.DicId.Value]
                };
            }
            else if ( col.FuncType == DataFuncType.选项值 )
            {
                return new RecordVal
                {
                    value = value,
                    dataType = col.DataType,
                    funcType = col.FuncType,
                    items = body.Items[col.ColId]
                };
            }
            return value;
        }
    }
}
